//
//  XLNetworking.m
//  MPTinyAppLearn
//
//  Created by xl on 2019/10/29.
//  Copyright © 2019 xl. All rights reserved.
//

#import "XLNetworking.h"
#import <AFNetworking.h>
#import <CommonCrypto/CommonDigest.h>
#import "NSData+Image.h"
#import "XLAES128.h"
#import "UIDevice+XL.h"

@interface NSString (md5)

+ (NSString *)networking_md5:(NSString *)string;

@end

@implementation NSString (md5)

+ (NSString *)networking_md5:(NSString *)string {
    if (string == nil || [string length] == 0) {
        return nil;
    }
    
    unsigned char digest[CC_MD5_DIGEST_LENGTH], i;
    CC_MD5([string UTF8String], (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], digest);
    NSMutableString *ms = [NSMutableString string];
    
    for (i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
        [ms appendFormat:@"%02x", (int)(digest[i])];
    }
    
    return [ms copy];
}

@end

static NSDictionary *_httpHeaders = nil;                    // http头，默认nil
static XLResponseType _responseType = XLResponseTypeJSON;   // 响应类型，默认JSON
static XLRequestType  _requestType  = XLRequestTypePlainText;    // 请求类型，默认JSON
static XLNetworkReachabilityStatus _networkStatus = XLNetworkReachabilityStatusUnkonw;  // 网络状态，默认未知网络
static NSTimeInterval _timeout = 30.0f;                     // 请求超时时间，默认60s
static NSMutableArray *_requestTasks;                       // 请求任务


@implementation XLNetworking

+ (NSMutableArray *)allTasks {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (_requestTasks == nil) {
            _requestTasks = [[NSMutableArray alloc] init];
        }
    });
    return _requestTasks;
}

+ (AFHTTPSessionManager *)manager {
    AFHTTPSessionManager *manager = nil;
    manager = [AFHTTPSessionManager manager];
    
    //请求类型
    switch (_requestType) {
        case XLRequestTypeJSON:
            manager.requestSerializer = [AFJSONRequestSerializer serializer];
            break;
        case XLRequestTypePlainText:
        manager.requestSerializer = [AFHTTPRequestSerializer serializer];
        break;
        default:
            break;
    }
    
    //响应类型
    switch (_responseType) {
        case XLResponseTypeJSON:
            manager.responseSerializer = [AFJSONResponseSerializer serializer];
            break;
        case XLResponseTypeXML:
        manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
        break;
        case XLResponseTypeData:
        manager.responseSerializer = [AFHTTPResponseSerializer serializer];
        break;
        default:
            break;
    }
    
    //设定UTF8编码
    manager.requestSerializer.stringEncoding = NSUTF8StringEncoding;
    
    //添加HTTP头
    for (NSString *key in _httpHeaders.allKeys) {
        if (_httpHeaders[key] != nil) {
            [manager.requestSerializer setValue:_httpHeaders[key] forHTTPHeaderField:key];
        }
    }
    
    //接收请求类型
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json",
                                                                              @"text/html",
                                                                              @"text/json",
                                                                              @"text/plain",
                                                                              @"text/javascript",
                                                                              @"text/xml",
                                                                              @"image/*"]];
    // 设置请求超时
    manager.requestSerializer.timeoutInterval = _timeout;
    
    //设置允许同时并发数量，过大容易出问题
    manager.operationQueue.maxConcurrentOperationCount = 4;
    
    manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
    manager.securityPolicy.validatesDomainName = NO;
    manager.securityPolicy.allowInvalidCertificates = YES;
    
    [self detectNetwork];
    return manager;
    
}

+ (void)detectNetwork {
    AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    [reachabilityManager startMonitoring];
    
    [reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        if (status == AFNetworkReachabilityStatusNotReachable) {
            _networkStatus = XLNetworkReachabilityStatusNotReachable;
        } else if (status == AFNetworkReachabilityStatusUnknown) {
            _networkStatus = XLNetworkReachabilityStatusUnkonw;
        } else if (status == AFNetworkReachabilityStatusReachableViaWWAN) {
            _networkStatus = XLNetworkReachabilityStatusReachableViaWWAN;
        } else if (status == AFNetworkReachabilityStatusReachableViaWiFi) {
            _networkStatus = XLNetworkReachabilityStatusReachableViaWiFi;
        }
    }];
}

+ (void)successResponse:(id)responseData
               dataTask:(NSURLSessionDataTask *)dataTask
               callback:(XLResponseSuccess)success {
    success(XLServerRequestsStatusSuccess, _networkStatus, responseData);
}

+ (void)failResponse:(id)responseData callback:(XLResponseFail)fail error:(NSError *)error {
    fail(XLServerRequestsStatusFail, _networkStatus, responseData, error);
}

+ (XLURLSessionTask *)requestWithUrl:(NSString *)url
                               image:(UIImage *)image
                            fileName:(NSString *)fileName
                                name:(NSString *)name
                          httpMethod:(XLHttpMedthType)httpMethod
                              params:(NSDictionary *)params
                            progress:(XLLoadProgress)progress
                             success:(XLResponseSuccess)success
                                fail:(XLResponseFail)fail {
    //初始化配置
    [self configCommonHttpHeaders:_httpHeaders];
    AFHTTPSessionManager *manager = [self manager];
    
    XLURLSessionTask *session = nil;
    switch (httpMethod) {
        case XLHttpMedthTypeGET:
            [self getWithManmger:manager absolute:url session:session Url:url params:params progress:progress success:success fail:fail];
            break;
        case XLHttpMedthTypePOST:
            [self postWithManmger:manager absolute:url session:session Url:url params:params progress:progress success:success fail:fail];
            break;
        case XLHttpMedthTypeUploadImage:
            [self uploadWithImage:image url:url fileName:fileName name:name parameters:params progress:progress success:success fail:fail];
            break;
        default:
            break;
    }
    // 添请求加任务
    if (session) {
        [[self allTasks] addObject:session];
    }
    return session;
}

+ (void)getWithManmger:(AFHTTPSessionManager *)manager
              absolute:(NSString *)absolute
               session:(XLURLSessionTask *)session
                   Url:(NSString *)url
                params:(NSDictionary *)params
              progress:(XLLoadProgress)progress
               success:(XLResponseSuccess)success
                  fail:(XLResponseFail)fail {
    session = [manager GET:url parameters:params headers:_httpHeaders progress:^(NSProgress * _Nonnull downloadProgress) {
        // 请求进度
        if (progress) {
            progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
        }
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        [self cacheResponseObject:responseObject request:task.currentRequest  parameters:nil];
        // 成功回调
        [self successResponse:responseObject  dataTask:task callback:success];
        XLLog(@"task%@", task);
        XLLog(@"responseObject%@", responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        XLLog(@"error:%@", error);
        // 删除请求任务
        [[self allTasks] removeObject:task];
        // 缓存响应
        id response = [XLNetworking cahceResponseWithURL:absolute parameters:params];
        // 失败回调
        [self failResponse:response callback:fail error:error];
    }];
    [session resume];
}

+ (void)postWithManmger:(AFHTTPSessionManager *)manager
              absolute:(NSString *)absolute
               session:(XLURLSessionTask *)session
                   Url:(NSString *)url
                params:(NSDictionary *)params
              progress:(XLLoadProgress)progress
               success:(XLResponseSuccess)success
                  fail:(XLResponseFail)fail {
    session = [manager POST:url parameters:params headers:_httpHeaders progress:^(NSProgress * _Nonnull uploadProgress) {
        if (progress) {
            progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
        }
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        [self cacheResponseObject:responseObject request:task.currentRequest  parameters:params];
        // 成功回调
        XLLog(@"task%@", task);
        XLLog(@"responseObject%@", responseObject);
        [self successResponse:responseObject  dataTask:task callback:success];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        XLLog(@"error:%@", error);
        // 删除请求任务
        [[self allTasks] removeObject:task];
        // 缓存响应
        id response = [XLNetworking cahceResponseWithURL:absolute parameters:params];
        // 失败回调
        [self failResponse:response callback:fail error:error];
    }];
    [session resume];
}

+ (void)uploadImageWithManmger:(AFHTTPSessionManager *)manager
                 absolute:(NSString *)absolute
                  session:(XLURLSessionTask *)session
                      Url:(NSString *)url
                   params:(NSDictionary *)params
                    image:(UIImage *)image
                 filename:(NSString *)filename
                     name:(NSString *)name
                 progress:(XLLoadProgress)progress
                  success:(XLResponseSuccess)success
                     fail:(XLResponseFail)fail {
    session = [manager POST:url parameters:params headers:_httpHeaders constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        NSData *imageData = UIImageJPEGRepresentation(image, 1);
        NSString *imageFileName = filename;
        if (filename == nil || ![filename isKindOfClass:[NSString class]] || filename.length == 0) {
            NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
            formatter.dateFormat = @"yyyyMMddHHmmss";
            NSString *str = [formatter stringFromDate:[NSDate date]];
            imageFileName = [NSString stringWithFormat:@"%@.jpg", str];
         }
        // 上传图片，以文件流的格式
        [formData appendPartWithFileData:imageData name:name fileName:imageFileName mimeType:@"image/jpeg"];
    } progress:^(NSProgress * _Nonnull uploadProgress) {
        // 上传进度
        if (progress) {
            progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
        }
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        // 删除任务
        [[self allTasks] removeObject:task];
        // 成功日志
//        [self logWithSuccessResponse:responseObject url:absolute params:params];
        // 成功回调
        [self successResponse:responseObject  dataTask:task callback:success];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        // 删除任务
        [[self allTasks] removeObject:task];
        // 失败日志
//        [self logWithFailError:error url:absolute params:params];
        // 失败回调
        [self failResponse:nil callback:fail error:error];
    }];
    [session resume];
}

#pragma mark - Public
+ (void)configCommonHttpHeaders:(NSDictionary *)httpHeaders {
    _httpHeaders = [self checkExtraDeviceParametersInDic:httpHeaders];
}

+ (void)updateCustomHeaderDicWithToken:(NSString *)token {
    if (ISEmptyString(token)) {
        return; //没有返回这些值的话直接退出
    }
    NSMutableDictionary *headerDic = [[NSMutableDictionary alloc] init];
    if (!ISEmptyString(token)) {
        [headerDic setObject:token forKey:@"authorization"];
    }
    [XLNetworking configCommonHttpHeaders:headerDic];

}

+ (NSTimeInterval)configTimeout:(NSTimeInterval)timeout {
    return _timeout = timeout;
}
+ (NSURLSessionTask *)getWithUrl:(NSString *)url
                          params:(NSDictionary *)params
                         success:(XLResponseSuccess)success
                            fail:(XLResponseFail)fail {
    return [self requestWithUrl:url image:nil fileName:nil name:nil httpMethod:XLHttpMedthTypeGET params:params progress:nil success:success fail:fail];
}

+ (NSURLSessionTask *)getWithUrl:(NSString *)url
                          params:(NSDictionary *)params
                        progress:(XLLoadProgress)progress
                         success:(XLResponseSuccess)success fail:(XLResponseFail)fail {
    return [self requestWithUrl:url image:nil fileName:nil name:nil httpMethod:XLHttpMedthTypeGET params:params progress:progress success:success fail:fail];
}

+ (NSURLSessionTask *)postWithUrl:(NSString *)url
                           params:(NSDictionary *)params
                          success:(XLResponseSuccess)success
                             fail:(XLResponseFail)fail {
    return [self requestWithUrl:url image:nil fileName:nil name:nil httpMethod:XLHttpMedthTypePOST params:params progress:nil success:success fail:fail];
}

+ (NSURLSessionTask *)postWithUrl:(NSString *)url
                           params:(NSDictionary *)params
                         progress:(XLLoadProgress)progress
                          success:(XLResponseSuccess)success
                             fail:(XLResponseFail)fail {
    return [self requestWithUrl:url image:nil fileName:nil name:nil httpMethod:XLHttpMedthTypePOST params:params progress:progress success:success fail:fail];
}

+ (NSURLSessionTask *)uploadWithImage:(UIImage *)image
                                  url:(NSString *)url
                             fileName:(NSString *)fileName
                                 name:(NSString *)name
                           parameters:(NSDictionary *)parameters
                             progress:(XLLoadProgress)progress
                              success:(XLResponseSuccess)success
                                 fail:(XLResponseFail)fail {
    return [self requestWithUrl:url image:image fileName:fileName name:name httpMethod:XLHttpMedthTypePOST params:parameters progress:progress success:success fail:fail];
}

#pragma mark - 缓存响应
+ (void)cacheResponseObject:(id)responseObject request:(NSURLRequest *)request parameters:params {
    if (request && responseObject && ![responseObject isKindOfClass:[NSNull class]]) {
        NSString *directoryPath = cachePath();
        
        NSError *error = nil;
        
        if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
            [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
                                      withIntermediateDirectories:YES
                                                       attributes:nil
                                                            error:&error];
            if (error) {
                XLLog(@"create cache dir error: %@\n", error);
                return;
            }
        }
        
        NSString *absoluteURL = [self generateGETAbsoluteURL:request.URL.absoluteString params:params];
        NSString *key = [NSString networking_md5:absoluteURL];
        NSString *path = [directoryPath stringByAppendingPathComponent:key];
        NSDictionary *dict = (NSDictionary *)responseObject;
        
        NSData *data = nil;
        if ([dict isKindOfClass:[NSData class]]) {
            data = responseObject;
        } else {
            data = [NSJSONSerialization dataWithJSONObject:dict
                                                   options:NSJSONWritingPrettyPrinted
                                                     error:&error];
        }
        
        if (data && error == nil) {
            BOOL isOk = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
            if (isOk) {
                XLLog(@"cache file ok for request: %@\n", absoluteURL);
            } else {
                XLLog(@"cache file error for request: %@\n", absoluteURL);
            }
        }
    }
}

+ (id)cahceResponseWithURL:(NSString *)url parameters:params {
    id cacheData = nil;
    
    if (url) {
        // 缓存路径
        NSString *directoryPath = cachePath();
        // 生成get绝对路径url
        NSString *absoluteURL = [self generateGETAbsoluteURL:url params:params];
        NSString *key = [NSString networking_md5:absoluteURL];
        NSString *path = [directoryPath stringByAppendingPathComponent:key];
        
        NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
        if (data) {
            cacheData = data;
            XLLog(@"Read data from cache for url: %@\n", url);
        }
    }
    
    return cacheData;
}


static inline NSString *cachePath() {
    return [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/XLNetworkingCaches"];
}


+ (NSString *)generateGETAbsoluteURL:(NSString *)url params:(id)params {
    if (params == nil || ![params isKindOfClass:[NSDictionary class]] || [(NSArray *)params count] == 0) {
        return url;
    }
    
    NSString *queries = @"";
    for (NSString *key in params) {
        id value = [params objectForKey:key];
        
        if ([value isKindOfClass:[NSDictionary class]]) {
            continue;
        } else if ([value isKindOfClass:[NSArray class]]) {
            continue;
        } else if ([value isKindOfClass:[NSSet class]]) {
            continue;
        } else {
            queries = [NSString stringWithFormat:@"%@%@=%@&",
                       (queries.length == 0 ? @"&" : queries),
                       key,
                       value];
        }
    }
    
    if (queries.length > 1) {
        queries = [queries substringToIndex:queries.length - 1];
    }
    
    if (([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) && queries.length > 1) {
        if ([url rangeOfString:@"?"].location != NSNotFound || [url rangeOfString:@"#"].location != NSNotFound) {
            url = [NSString stringWithFormat:@"%@%@", url, queries];
        } else {
            queries = [queries substringFromIndex:1];
            url = [NSString stringWithFormat:@"%@?%@", url, queries];
        }
    }
    
    return url.length == 0 ? queries : url;
}

//上传认证图片
+ (void)uploadCertifyImageWithUrl:(NSString *)url
                           params:(NSDictionary *)params
                       stuPicArrs:(NSArray *)picArrs
                      idCardFront:(UIImage *)idCardFront
                     idCardBehind:(UIImage *)idCardBehind
                         mimeType:(NSString *)mimeType
                         progress:(XLLoadProgress)progress
                          success:(XLResponseSuccess)success
                             fail:(XLResponseFail)fail {
    AFHTTPSessionManager *manager = [self manager];
    XLURLSessionTask *session = nil;
    session = [manager POST:url parameters:params headers:_httpHeaders constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        formatter.dateFormat = @"yyyyMMddHHmmss";
        NSString *str = [formatter stringFromDate:[NSDate date]];
        NSString *imageFileName = [NSString stringWithFormat:@"%@.jpg", str];
        for (int i = 0; i < picArrs.count; i ++) {
            NSData *imageData = [NSData reSizeImageData:picArrs[i] maxImageSize:750 maxSizeWithKB:200];
            // 上传图片，以文件流的格式
            [formData appendPartWithFileData:imageData name:@"stuPictures" fileName:imageFileName mimeType:mimeType];
        }
        NSData *idCardFrontData = [NSData reSizeImageData:idCardFront maxImageSize:750 maxSizeWithKB:200];
        // 上传图片，以文件流的格式
        [formData appendPartWithFileData:idCardFrontData name:@"idCardFrontPicture" fileName:imageFileName mimeType:mimeType];
        NSData *idCardBehindData = [NSData reSizeImageData:idCardBehind maxImageSize:750 maxSizeWithKB:200];
        // 上传图片，以文件流的格式
        [formData appendPartWithFileData:idCardBehindData name:@"idCardBehindPicture" fileName:imageFileName mimeType:mimeType];
    } progress:^(NSProgress * _Nonnull uploadProgress) {
        // 上传进度
        if (progress) {
            progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
        }
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        // 删除任务
        [[self allTasks] removeObject:task];
        // 成功回调
        [self successResponse:responseObject  dataTask:task callback:success];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        // 删除任务
        [[self allTasks] removeObject:task];
        // 失败回调
        [self failResponse:nil callback:fail error:error];
    }];
    [session resume];
}

+ (NSDictionary *)checkExtraDeviceParametersInDic:(NSDictionary *)oldDic
{
    NSMutableDictionary *dic = [[NSMutableDictionary alloc] initWithDictionary:oldDic];
    
    if (![oldDic objectForKey:@"appVersion"]) {
        NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
        // app版本
        NSString *appVersion = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
        if (!ISEmptyString(appVersion)) {
            [dic setObject:appVersion forKey:@"appVersion"];
        }
    }
    
    if (![oldDic objectForKey:@"brand"]) {
        //机器类型：iPhone
        NSString * brand = [[UIDevice currentDevice] model];
        if (!ISEmptyString(brand)) {
            [dic setObject:brand forKey:@"brand"];
        }
    }
    
    //手机型号对应的手机名称
    if (![oldDic objectForKey:@"model"]) {
        NSString *model = [UIDevice currentDevice].machineType;
        if (!ISEmptyString(model)) {
            [dic setObject:model forKey:@"model"];
        }
    }

    if (![oldDic objectForKey:@"systemVersion"]) {
        //手机系统版本：11.1.2
        NSString* systemVersion = [[UIDevice currentDevice] systemVersion];
        if (!ISEmptyString(systemVersion)) {
            [dic setObject:systemVersion forKey:@"systemVersion"];
        }
    }
    
    if (![oldDic objectForKey:@"uniqueId"]) {
        NSString *uuid = [UIDevice currentDevice].uuid;
        if (!ISEmptyString(uuid)) {
            [dic setObject:uuid forKey:@"uniqueId"];
        }
    }
    
    //本机当前IP地址
    if (![oldDic objectForKey:@"localIP"]) {
        NSString *localIP = [UIDevice currentDevice].machineLocalIP;
        if (!ISEmptyString(localIP)) {
            [dic setObject:localIP forKey:@"localIP"];
        }
    }
    
    //手机运营商
    if (![oldDic objectForKey:@"operator"]) {
        NSString *operator = [UIDevice currentDevice].machineOperator;
        if (!ISEmptyString(operator)) {
            [dic setObject:operator forKey:@"operator"];
        }
    }
    
    //当前使用网络类型
    if (![oldDic objectForKey:@"networkType"]) {
        NSString *networkType = [UIDevice currentDevice].machineNetType;
        if (!ISEmptyString(networkType)) {
            [dic setObject:networkType forKey:@"networkType"];
        }
    }
    return dic;
}
@end
